package plugincleaner.preferences;

import java.io.File;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.core.runtime.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class MyDirectoryFieldEditor extends FieldEditor {
	public static final int VALIDATE_ON_KEY_STROKE = 0;
	public static final int VALIDATE_ON_FOCUS_LOST = 1;
	public static int UNLIMITED = -1;

	private boolean isValid;
	private boolean emptyStringAllowed = true;

	private int widthInChars = UNLIMITED;
	private int textLimit = UNLIMITED;
	private int validateStrategy = VALIDATE_ON_KEY_STROKE;

	private Text textField;
	private Button changeButton;

	protected String oldValue;
	private String errorMessage;
	private String changeButtonText;

	private PluginTableController gController;

	protected MyDirectoryFieldEditor() {
	}

	public MyDirectoryFieldEditor(String name, String labelText, int width,
			int strategy, Composite parent, PluginTableController controller) {
		init(name, labelText);
		widthInChars = width;
		isValid = false;
		errorMessage = JFaceResources
				.getString("DirectoryFieldEditor.errorMessage");
		setChangeButtonText("Browse");
		setValidateStrategy(VALIDATE_ON_FOCUS_LOST);
		createControl(parent);

		this.gController = controller;
	}

	public MyDirectoryFieldEditor(String name, String labelText, int width,
			Composite parent, PluginTableController controller) {
		this(name, labelText, width, VALIDATE_ON_KEY_STROKE, parent, controller);
	}

	public MyDirectoryFieldEditor(String name, String labelText,
			Composite parent, PluginTableController controller) {
		this(name, labelText, UNLIMITED, parent, controller);
	}

	private File getDirectory(File startingDirectory) {
		DirectoryDialog fileDialog = new DirectoryDialog(getShell(), SWT.OPEN);
		if (startingDirectory != null) {
			fileDialog.setFilterPath(startingDirectory.getPath());
		}
		String dir = fileDialog.open();
		if (dir != null) {
			dir = dir.trim();
			if (dir.length() > 0) {
				return new File(dir);
			}
		}

		return null;
	}

	protected void valueChanged() {
		setPresentsDefaultValue(false);
		boolean oldState = isValid;
		refreshValidState();

		if (isValid != oldState) {
			fireStateChanged(IS_VALID, oldState, isValid);
		}

		String newValue = textField.getText();
		if (!newValue.equals(oldValue)) {
			fireValueChanged(VALUE, oldValue, newValue);
			oldValue = newValue;
		}
	}

	protected String changePressed() {
		File f = new File(getTextControl().getText());
		if (!f.exists()) {
			f = null;
		}
		File d = getDirectory(f);
		if (d == null) {
			return null;
		}

		return d.getAbsolutePath();
	}

	protected Button getChangeControl(Composite parent) {
		if (changeButton == null) {
			changeButton = new Button(parent, SWT.PUSH);
			if (changeButtonText == null) {
				changeButtonText = JFaceResources.getString("openChange"); //$NON-NLS-1$
			}
			changeButton.setText(changeButtonText);
			changeButton.setFont(parent.getFont());
			changeButton.addSelectionListener(new SelectionAdapter() {
				public void widgetSelected(SelectionEvent evt) {
					String newValue = changePressed();
					if (newValue != null) {
						setStringValue(newValue);
						
						// UPDATE TABLES
						gController.cleanAll();
						gController.updateFoundPluginTable(newValue);
					}
				}
			});
			changeButton.addDisposeListener(new DisposeListener() {
				public void widgetDisposed(DisposeEvent event) {
					changeButton = null;
				}
			});
		} else {
			checkParent(changeButton, parent);
		}
		return changeButton;

	}

	protected boolean doCheckState() {
		String fileName = getTextControl().getText();
		fileName = fileName.trim();
		if (fileName.length() == 0 && isEmptyStringAllowed()) {
			return true;
		}
		File file = new File(fileName);
		return file.isDirectory();
	}

	protected Shell getShell() {
		if (changeButton == null) {
			return null;
		}
		return changeButton.getShell();
	}

	protected boolean checkState() {
		boolean result = false;
		if (emptyStringAllowed) {
			result = true;
		}

		if (textField == null) {
			result = false;
		}

		String txt = textField.getText();

		result = (txt.trim().length() > 0) || emptyStringAllowed;

		// call hook for subclasses
		result = result && doCheckState();

		if (result) {
			clearErrorMessage();
		} else {
			showErrorMessage(errorMessage);
		}

		return result;
	}

	protected Text getTextControl() {
		return textField;
	}

	public Text getTextControl(Composite parent) {
		if (textField == null) {
			textField = new Text(parent, SWT.SINGLE | SWT.BORDER);
			textField.setFont(parent.getFont());
			switch (validateStrategy) {
			case VALIDATE_ON_KEY_STROKE:
				textField.addKeyListener(new KeyAdapter() {
					@Override
					public void keyReleased(KeyEvent e) {
						valueChanged();
					}
				});
				textField.addFocusListener(new FocusAdapter() {
					// Ensure that the value is checked on focus loss in case we
					// missed a keyRelease or user hasn't released key.
					// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=214716
					@Override
					public void focusLost(FocusEvent e) {
						valueChanged();
					}
				});

				break;
			case VALIDATE_ON_FOCUS_LOST:
				textField.addKeyListener(new KeyAdapter() {
					@Override
					public void keyPressed(KeyEvent e) {
						clearErrorMessage();
					}
				});
				textField.addFocusListener(new FocusAdapter() {
					@Override
					public void focusGained(FocusEvent e) {
						refreshValidState();
					}

					@Override
					public void focusLost(FocusEvent e) {
						valueChanged();
						clearErrorMessage();
					}
				});
				break;
			default:
				Assert.isTrue(false, "Unknown validate strategy");//$NON-NLS-1$
			}
			textField.addDisposeListener(new DisposeListener() {
				public void widgetDisposed(DisposeEvent event) {
					textField = null;
				}
			});
			if (textLimit > 0) {// Only set limits above 0 - see SWT spec
				textField.setTextLimit(textLimit);
			}
		} else {
			checkParent(textField, parent);
		}
		return textField;
	}

	public boolean isEmptyStringAllowed() {
		return emptyStringAllowed;
	}

	public void setEmptyStringAllowed(boolean b) {
		emptyStringAllowed = b;
	}

	public void setErrorMessage(String message) {
		errorMessage = message;
	}

	public void setStringValue(String value) {
		if (textField != null) {
			if (value == null) {
				value = "";//$NON-NLS-1$
			}
			oldValue = textField.getText();
			if (!oldValue.equals(value)) {
				textField.setText(value);
				valueChanged();
			}
		}
	}

	public void setTextLimit(int limit) {
		textLimit = limit;
		if (textField != null) {
			textField.setTextLimit(limit);
		}
	}

	public void setChangeButtonText(String text) {
		Assert.isNotNull(text);
		changeButtonText = text;
		if (changeButton != null) {
			changeButton.setText(text);
			Point prefSize = changeButton.computeSize(SWT.DEFAULT, SWT.DEFAULT);
			GridData data = (GridData) changeButton.getLayoutData();
			data.widthHint = Math.max(SWT.DEFAULT, prefSize.x);
		}
	}

	public void setValidateStrategy(int value) {
		Assert.isTrue(value == VALIDATE_ON_FOCUS_LOST
				|| value == VALIDATE_ON_KEY_STROKE);
		validateStrategy = value;
	}

	public void showErrorMessage() {
		showErrorMessage(errorMessage);
	}

	public String getErrorMessage() {
		return errorMessage;
	}

	public String getStringValue() {
		if (textField != null) {
			return textField.getText();
		}

		return getPreferenceStore().getString(getPreferenceName());
	}

	@Override
	protected void adjustForNumColumns(int numColumns) {
		((GridData) getTextControl().getLayoutData()).horizontalSpan = numColumns - 2;
	}

	@Override
	protected void doFillIntoGrid(Composite parent, int numColumns) {
		getLabelControl(parent);

		textField = getTextControl(parent);
		GridData gd = new GridData();
		gd.horizontalSpan = numColumns - 1;
		if (widthInChars != UNLIMITED) {
			GC gc = new GC(textField);
			try {
				Point extent = gc.textExtent("X");//$NON-NLS-1$
				gd.widthHint = widthInChars * extent.x;
			} finally {
				gc.dispose();
			}
		} else {
			gd.horizontalAlignment = GridData.FILL;
			gd.grabExcessHorizontalSpace = true;
		}
		textField.setLayoutData(gd);

		changeButton = getChangeControl(parent);
		gd.horizontalAlignment = GridData.CENTER;
		int widthHint = convertHorizontalDLUsToPixels(changeButton,
				IDialogConstants.BUTTON_WIDTH);
		gd.widthHint = Math.max(widthHint, changeButton.computeSize(
				SWT.DEFAULT, SWT.DEFAULT, true).x);
		changeButton.setLayoutData(gd);
	}

	@Override
	protected void doLoad() {
		if (textField != null) {
			String value = getPreferenceStore().getString(getPreferenceName());
			textField.setText(value);
			oldValue = value;
		}
	}

	@Override
	protected void doLoadDefault() {
		if (textField != null) {
			String value = getPreferenceStore().getDefaultString(
					getPreferenceName());
			textField.setText(value);
		}
		valueChanged();
	}

	@Override
	protected void doStore() {
		getPreferenceStore().setValue(getPreferenceName(), textField.getText());
	}

	@Override
	public boolean isValid() {
		return isValid;
	}

	@Override
	protected void refreshValidState() {
		isValid = checkState();
	}

	@Override
	public void setFocus() {
		if (textField != null) {
			textField.setFocus();
		}
	}

	@Override
	public int getNumberOfControls() {
		return 3;
	}

	@Override
	public void setEnabled(boolean enabled, Composite parent) {
		super.setEnabled(enabled, parent);
		if (changeButton != null) {
			changeButton.setEnabled(enabled);
		}

		getTextControl(parent).setEnabled(enabled);
	}

}
